package cn.xie.blog.serviceuser.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.xie.blog.model.common.entity.login.UserEntity;
import cn.xie.blog.model.common.enums.response.ResponseCodeEnum;
import cn.xie.blog.model.common.param.login.LoginParam;
import cn.xie.blog.model.common.param.registered.RegisteredParam;
import cn.xie.blog.model.common.thradlocal.UserInfo;
import cn.xie.blog.model.common.ttl.UserContext;
import cn.xie.blog.model.common.vo.UserDetailsVO;
import cn.xie.blog.serverutil.constant.common.RequestConstant;
import cn.xie.blog.serverutil.constant.redis.RedisConstant;
import cn.xie.blog.serverutil.exception.BlogException;
import cn.xie.blog.serverutil.util.*;
import cn.xie.blog.serviceuser.common.util.constant.UserConstant;
import cn.xie.blog.serviceuser.mapper.UserMapper;
import cn.xie.blog.serviceuser.service.UserService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange;

import java.net.http.HttpRequest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static cn.xie.blog.serverutil.constant.redis.RedisConstant.ACCOUNT_LOCK_PREFIX;
import static cn.xie.blog.serverutil.constant.redis.RedisConstant.WRONG_PWD_COUNT_PREFIX;

/**
 * @author: xiaoxie
 * create: 2025-04-22 20:56
 * @BelongsProject: blog-parent
 * @BelongsPackage: cn.xie.blog.serviceuser.service.impl
 * description:
 */

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public String login(LoginParam loginParam) {
        UserEntity userEntity = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                .eq(UserEntity::getUsername, loginParam.getUsername())
                .eq(UserEntity::getIsDelete, 0));

        if (userEntity == null) {
            throw new BlogException(ResponseCodeEnum.ACCOUNT_NOT_REGISTERED);
        }

        // 检查用户是否被锁定
        if (isAccountLocked(userEntity.getUsername())) {
            throw new BlogException(ResponseCodeEnum.ACCOUNT_LOCKED);
        }

        try {
            boolean flag = EncryptUtils.verifyPassword(loginParam.getPassword(),userEntity.getSalt(),userEntity.getPasswordHash());
            if (!flag) {
                // 密码错误记录失败次数
                recordFailedLoginAttempt(userEntity.getUsername());
                throw new BlogException(ResponseCodeEnum.ACCOUNT_OR_PASSWORD_ERROR);
            }
            // 登录成功，清除失败记录
            clearFailedLoginRecord(userEntity.getUsername());
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }

        // 生成token
        String token = jwtUtil.generateToken(userEntity.getUsername(), new UserContext(userEntity.getUserId(), userEntity.getUsername(),
                userEntity.getEmail(),userEntity.getAvatarUrl(), userEntity.getIsAdmin()));
        try {
            if (loginParam.getIsAdmin() == 1) {
                redisUtil.put(RedisConstant.ADMIN_LOGIN_KEY_PREFIX + userEntity.getUsername(), token, RedisConstant.USER_LOGIN_KEY_TIMEOUT);
            } else {
                redisUtil.put(RedisConstant.USER_LOGIN_KEY_PREFIX + userEntity.getUsername(), token, RedisConstant.USER_LOGIN_KEY_TIMEOUT);
            }
        } catch (Exception e) {
            log.error("Redis 操作失败", e);
            throw new RuntimeException("Redis 操作失败", e);
        }

        return token;
    }


    @Override
    public Boolean registered(RegisteredParam registeredParam) {
        if (ObjectUtil.isNotNull(registeredParam)) {
            String codeFromRedis = (String)   redisUtil.get(RedisConstant.REGISTERED_CODE_KEY_PREFIX + registeredParam.getEmail());
            if (codeFromRedis == null || !StrUtil.equalsIgnoreCase(registeredParam.getCode(), codeFromRedis)) {
                throw new BlogException(ResponseCodeEnum.CODE_ERROR);
            }
            // 确认用户名和邮箱唯一
            UserEntity userName = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                    .eq(UserEntity::getEmail, registeredParam.getUsername()));
            if(userName != null) {
                throw new BlogException(ResponseCodeEnum.USERNAME_EXISTED);
            }
            UserEntity email = userMapper.selectOne(Wrappers.<UserEntity>lambdaQuery()
                    .eq(UserEntity::getEmail, registeredParam.getEmail()));
            if(email != null) {
                throw new BlogException(ResponseCodeEnum.EMAIL_EXISTED);
            }
            // 验证码验证通过后，删除 Redis 中的验证码
            redisUtil.del(RedisConstant.REGISTERED_CODE_KEY_PREFIX + registeredParam.getEmail());
            String salt = EncryptUtils.generateSalt();
            registeredParam.setSalt(salt);
            try {
                String encryptPassword = EncryptUtils.encryptPassword(registeredParam.getPassword(), salt);
                registeredParam.setPassword(encryptPassword);
                registeredParam.setAvatarUrl(UserConstant.USER_URL);
                this.save(toDO(registeredParam));
                return true;
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            } catch (InvalidKeySpecException e) {
                throw new RuntimeException(e);
            }
        }
        return false;
    }

    private UserEntity toDO(RegisteredParam registeredParam) {
        UserEntity userEntity = new UserEntity();
        userEntity.setUsername(registeredParam.getUsername());
        userEntity.setEmail(registeredParam.getEmail());
        userEntity.setPasswordHash(registeredParam.getPassword());
        userEntity.setAvatarUrl(registeredParam.getAvatarUrl());
        userEntity.setCreatedAt(LocalDateTime.now());
        userEntity.setSalt(registeredParam.getSalt());
        return userEntity;
    }

    @Override
    public Boolean getCode(String mail) {
        try {
            String code = VerifyCodeUtil.registeredCode();
            boolean isMailSent = MailUtil.sendEmail(mail, code);
            if (!isMailSent) {
                throw new BlogException(ResponseCodeEnum.MAIL_SEND_FAILED);
            }
            redisUtil.put(RedisConstant.REGISTERED_CODE_KEY_PREFIX + mail, code, RedisConstant.LOGIN_CODE_TIMEOUT);
            return true;
        } catch (Exception e) {
            log.error("获取验证码失败", e);
            throw new RuntimeException("获取验证码失败", e);
        }
    }

    @Override
    public UserDetailsVO getUserDetailsById(String userId) {
        UserEntity userEntity = userMapper.selectById(userId);
        return Optional.ofNullable(userEntity)
                .map(e -> {
                    UserDetailsVO userDetailsVO = new UserDetailsVO();
                    userDetailsVO.setUserId(e.getUserId());
                    userDetailsVO.setUsername(e.getUsername());
                    userDetailsVO.setEmail(e.getEmail());
                    userDetailsVO.setAvatarUrl(e.getAvatarUrl());
                    userDetailsVO.setBio(e.getBio());
                    userDetailsVO.setCreatedAt(e.getCreatedAt());
                    userDetailsVO.setLastLogin(e.getLastLogin());
                    userDetailsVO.setIsAdmin(e.getIsAdmin());
                    userDetailsVO.setUpdateAt(e.getUpdateAt());
                    return userDetailsVO;
                })
                .orElseThrow(() -> new BlogException(ResponseCodeEnum.DATA_NOT_FOUND));
    }

    @Override
    public Boolean logout() {
        String json = (String) redisUtil.get(RedisConstant.ADMIN_Context_KEY_PREFIX + "admin");
        UserContext userContext = JSONUtil.parse(json).toBean(UserContext.class);
         if (userContext == null) {
            log.warn("UserContext 未设置");
            return false;
         }
            // 使用常量管理 Redis 键前缀
            String redisKeyPrefix = userContext.getIsAdmin() == 1? RedisConstant.ADMIN_LOGIN_KEY_PREFIX : RedisConstant.USER_LOGIN_KEY_PREFIX;
            String redisKey = redisKeyPrefix + userContext.getUserName();
            // 保存最后登录时间
        userMapper.update(null, Wrappers.<UserEntity>lambdaUpdate()
                .set(UserEntity::getLastLogin, LocalDateTime.now())
                .eq(UserEntity::getUsername, "admin"));
            // 尝试删除 Redis 键值对
           return redisUtil.del(redisKey);

    }

    // 增加一个方法，用于记录失败的登录尝试
    private void recordFailedLoginAttempt(String userName) {
        String wrongCountKey = RedisConstant.WRONG_PWD_COUNT_PREFIX + userName;
        String currentCount = (String) redisUtil.get(wrongCountKey);
        // 如果没有失败记录，初始化为1
        if (currentCount == null) {
            redisUtil.put(wrongCountKey, "1",RedisConstant.ACCOUNT_LOCK_KEY_TIMEOUT);
            return;
        }

        int count = Integer.parseInt(currentCount) + 1;
        if (count >= 3) {
            // 超过3次，设置账号锁定
            String lockKey = ACCOUNT_LOCK_PREFIX + userName;
            redisUtil.put(lockKey, "1",RedisConstant.ACCOUNT_LOCK_KEY_TIMEOUT);
            // 重置失败次数
            redisUtil.put(wrongCountKey, "0", RedisConstant.LOGIN_CODE_TIMEOUT);
        } else {
            // 更新失败次数
            redisUtil.put(wrongCountKey, String.valueOf(count),  RedisConstant.LOGIN_CODE_TIMEOUT);
        }
    }

    // 增加一个方法，用于检查账号是否被锁定
    private boolean isAccountLocked(String userName) {
        String lockKey = ACCOUNT_LOCK_PREFIX + userName;
        return redisUtil.hasKey(lockKey);
    }

    // 增加一个方法，用于清除失败登录记录
    private void clearFailedLoginRecord(String userName) {
        String wrongCountKey = WRONG_PWD_COUNT_PREFIX + userName;
        redisUtil.del(wrongCountKey);
    }

}
